package com.fang.security.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fang.security.common.CommonConstants;
import com.fang.security.common.base.ServiceCode;
import com.fang.security.common.exception.RException;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * @Description
 * @Author Bernie
 * @Date 2024/4/3 10:23
 **/
@Slf4j
public class JWTUtil {

    /**
     * token的密钥
     */
    private static final String TOKEN_SECRET = "f26e587c28064d0e855e72c0a6a0e618";

    /**
     * 校验token是否正确
     */
    public static boolean verify(String token) throws RException {
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (TokenExpiredException e) {
            log.error("token验证失败-------------->{}", "登录信息已过期");
//            throw new RException(ServiceCode.LOGIN_EXPIRED);
            return Boolean.FALSE;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RException(ServiceCode.SYSTEM_BUSY);
        }
    }

    /**
     * @param token:
     * @Description: 获取token中的用户Id
     * @Author: Bernie_fang
     * @Since: 2021/8/18 23:09
     * @return: java.lang.String
     **/
    public static String getUserId(String token) throws RException {
        try {
            DecodedJWT verify = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build().verify(token);
            Claim userId = verify.getClaim(CommonConstants.JWTConstants.USERID);
            return userId.asString();
        } catch (Exception e) {
            log.error("token解析获取userid出错------------->{}", e.getMessage());
            throw new RException(ServiceCode.SYSTEM_BUSY);
        }
    }

    /**
     * @param token:
     * @Author Bernie
     * @Description 获取token中的用户username
     * @Date 15:00 2024/4/15
     * @return: java.lang.String
     **/
    public static String getUsername(String token) throws RException {
        try {
            DecodedJWT verify = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build().verify(token);
            Claim username = verify.getClaim(CommonConstants.JWTConstants.USERNAME);
            return username.asString();
        } catch (Exception e) {
            log.error("token解析获取username出错------------->{}", e.getMessage());
            throw new RException(ServiceCode.SYSTEM_BUSY);
        }
    }

    public static Set<String> getRoles(String token) throws RException {
        try {
            DecodedJWT verify = JWT.require(getAlgorithm()).build().verify(token);
            Claim roles = verify.getClaim(CommonConstants.JWTConstants.ROLES);
            String rolesString = roles.asString();
            Set<String> roleSet = JSON.parseObject(rolesString, new TypeReference<Set<String>>() {});
            return roleSet;
        } catch (Exception e) {
            log.error("token解析获取roles出错------------->{}", e.getMessage());
            throw new RException(ServiceCode.SYSTEM_BUSY);
        }
    }

    private static Date getExpirationTime() {
        return new Date(System.currentTimeMillis() + CommonConstants.JWTConstants.EXPIRATION_TIME_MILLISECOND);
    }

    private static final Algorithm getAlgorithm() throws UnsupportedEncodingException {
        return Algorithm.HMAC256(TOKEN_SECRET);
    }

    private static final Map<String, Object> getJWTHeader() {
        Map<String, Object> header = new HashMap<>(2);
        header.put("typ", "JWT");
        header.put("alg", "HS256");
        return header;
    }

    public static String signToToken(String userId, Set<String> roles) throws RException {
        try {
            String rolesStr = JSON.toJSONString(roles);
            //过期时间
            Date expirationTime = getExpirationTime();
            //密钥及算法
            Algorithm algorithm = getAlgorithm();
            //设置头部信息
            Map<String, Object> header = getJWTHeader();
            //附带userId信息，生成签名
            return JWT.create()
                    .withHeader(header)
                    .withClaim(CommonConstants.JWTConstants.USERID, userId)
                    .withClaim(CommonConstants.JWTConstants.ROLES, rolesStr)
                    .withExpiresAt(expirationTime)  //设置过期时间
                    .sign(algorithm);
        } catch (Exception e) {
            log.error("token(roles) 生成失败，id-->{}", userId);
            throw new RException(ServiceCode.SYSTEM_BUSY);
        }
    }

    /**
     * @param username:
     * @param userId:
     * @Description: 生成签名
     * @Author: Bernie_fang
     * @Since: 2021/8/18 23:08
     * @return: java.lang.String
     **/
    public static String signToToken(String username, String userId) throws RException {
        try {
            //过期时间
            Date expirationTime = getExpirationTime();
            //密钥及算法
            Algorithm algorithm = getAlgorithm();
            //设置头部信息
            Map<String, Object> header = getJWTHeader();
            //附带userId信息，生成签名
            return JWT.create()
                    .withHeader(header)
                    .withClaim(CommonConstants.JWTConstants.USERNAME, username)
                    .withClaim(CommonConstants.JWTConstants.USERID, userId)
                    .withExpiresAt(expirationTime)  //设置过期时间
                    .sign(algorithm);
        } catch (Exception e) {
            log.error("token(userId) 生成失败，id-->{}", userId);
            throw new RException(ServiceCode.SYSTEM_BUSY);
        }
    }


    /**
     * Description: 验证原有token是否在半个小时以内过期,是返回true,否返回false
     *
     * @param verifyToken:
     * @Author: Bernie_fang
     * @Since: 2022/2/20 13:54
     * @return: java.lang.Boolean
     **/
    public static Boolean verifyExpiresInTwoHours(String verifyToken) throws UnsupportedEncodingException {
        Algorithm algorithm = getAlgorithm();
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt = verifier.verify(verifyToken);
        Date expiresAt = jwt.getExpiresAt();        //过期时间
        Long halfHours = new Date().getTime() + CommonConstants.JWTConstants.RESIDUE_EXPIRATION_TIME_MILLISECOND;
        if (expiresAt.getTime() > halfHours) {
            return false;
        }
        return true;
    }


}
